package lumis.service.wsrp.consumer;

import com.sun.portal.container.EntityID;
import com.sun.portal.container.ExecuteActionResponse;
import com.sun.portal.container.PortletLang;
import com.sun.portal.container.PortletType;
import com.sun.portal.container.PortletWindowContext;
import com.sun.portal.container.PortletWindowContextException;
import com.sun.portal.container.service.EventHolder;
import com.sun.portal.container.service.PublicRenderParameterHolder;
import com.sun.portal.container.service.policy.DistributionType;
import com.sun.portal.wsrp.common.WSRPSpecKeys;
import com.sun.portal.wsrp.common.WSRPUtility;

import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import javax.portlet.PortletPreferences;
import javax.portlet.PortletSession;
import javax.servlet.http.HttpServletRequest;

import lumis.portal.PortalException;
import lumis.portal.authentication.SessionConfig;
import lumis.portal.serviceinterface.IServiceInterfaceRequest;
import lumis.portal.transaction.ITransaction;
import lumis.portal.transaction.PortalTransactionFactory;
import lumis.portal.user.UserConfig;
import lumis.service.wsrp.consumer.producermanager.ProducerEntityManagerFactory;

/**
 * Implementation of portlet window context for remote portlets being consumed.
 * 
 * @version $Revision: 11180 $ $Date: 2009-12-04 18:13:02 -0200 (Fri, 04 Dec 2009) $
 * @since 5.5.0
 */
class RemotePortletWindowContextImpl implements PortletWindowContext
{
	/**
	 * Prefix for property used to store if a portlet window is the target of
	 * the request.
	 * <p>
	 * Property manipulated by WSRP container, using 
	 * {@link #getProperty(String)} and {@link #setProperty(String, Object)}.
	 * @since 5.5.0
	 */
	// currently no special treatment is being given to this property
	//private static final String PROPERTY_IS_TARGET_PREFIX = "com.sun.portal.container.isTarget.";
	
	/**
	 * Prefix for property used to store the render parameters for a 
	 * portlet window.
	 * <p>
	 * Property manipulated by WSRP container, using 
	 * {@link #getProperty(String)} and {@link #setProperty(String, Object)}.
	 * @since 5.5.0
	 */
	private static final String PROPERTY_RENDER_PARAMETERS_PREFIX = "com.sun.portal.portletcontainer.renderParameters.";
	
	/**
	 * Prefix for property used to store the public render parameters for a 
	 * portlet window.
	 * <p>
	 * Property manipulated by WSRP container, using 
	 * {@link #getProperty(String)} and {@link #setProperty(String, Object)}.
	 * @since 5.5.0
	 */
	private static final String PROPERTY_PUBLIC_RENDER_PARAMETERS_PREFIX = "com.sun.portal.portletcontainer.publicrenderparameters";
	
	private final IServiceInterfaceRequest interfaceRequest;
	private final ExecuteActionResponse executeActionResponse;
	private final String remotePortletHandle;
	private final String producerEntityId;
	private String userRepresentation = null;

	public RemotePortletWindowContextImpl(IServiceInterfaceRequest request, ExecuteActionResponse response) throws PortalException
	{
		interfaceRequest = request;
		executeActionResponse = response;
		
		ITransaction transaction = PortalTransactionFactory.createTransaction();
		try
		{
			transaction.begin();
			
			String serviceInterfaceInstanceId = request.getServiceInterfaceInstanceId();
			SessionConfig sessionConfig = request.getSessionConfig();
			remotePortletHandle = RemotePortletServiceInterface.getRemotePortletHandle(sessionConfig, serviceInterfaceInstanceId, transaction);
			
			String serviceInstanceId = request.getServiceInstanceConfig().getId();
			producerEntityId = ProducerEntityManagerFactory.getProducerEntityId(sessionConfig, serviceInstanceId, transaction);
			
			transaction.commit();
		}
		finally
		{
			transaction.dispose();
		}
	}

	public void setNavigationalState(String[] navigationalState)
	{
		String portletWindowName = interfaceRequest.getServiceInterfaceInstanceId();
		if (navigationalState != null)
		{
			Map<String, String[]> opaqueParams = new HashMap<String, String[]>();
			opaqueParams.put(WSRPSpecKeys.NAVIGATIONAL_STATE, navigationalState);
			setProperty(PROPERTY_RENDER_PARAMETERS_PREFIX + getPortletID(portletWindowName), opaqueParams);
		}
	}
	
	public void init(HttpServletRequest request)
	{
	}

	public String getConsumerID(String portletWindowName) throws PortletWindowContextException
	{
		return interfaceRequest.getServiceInstanceConfig().getId();
	}

	public String getProducerEntityID(String portletWindowName) throws PortletWindowContextException
	{
		return producerEntityId;
	}

	public String getPortletID(String portletWindowName)
	{
		return getPortletHandle(portletWindowName);
	}

	public String getLocaleString()
	{
		return WSRPUtility.toXMLLang(interfaceRequest.getLocale().toString());
	}

	public String getContentType()
	{
		return interfaceRequest.getResponseContentType();
	}

	public boolean isAuthless(HttpServletRequest request)
	{
		try
		{
			return UserConfig.USER_GUEST_ID.equals(interfaceRequest.getSessionConfig().getUserId());
		}
		catch (PortalException e)
		{
			throw new RuntimeException(e);
		}
	}

	public String getAuthenticationType()
	{
		return interfaceRequest.getAuthType();
	}

	public String getUserRepresentation()
	{
		// The order in which this is suppose to be done
		// 1. Check if userRepresentation is explicity set , If yes use it
		// always
		// 2. If user ID is null, Get from request - getPrincipal
		// 3. If userRepresentation is null, see if wsrp is sending it (in case
		// of resource URL)
		// 4. else return null.
		if (userRepresentation == null)
		{
			Principal principal = interfaceRequest.getUserPrincipal();
			if (principal != null)
			{
				userRepresentation = principal.getName();
			}
			else
			{
				userRepresentation = interfaceRequest.getParameter("wsrp.userID");
			}
		}
		return userRepresentation;
	}

	public Object getProperty(String name)
	{
		if (name == null)
			throw new IllegalArgumentException("Property name cannot be null");
		
		if (isSpecialProperty(name, PROPERTY_RENDER_PARAMETERS_PREFIX))
		{
			// return the map containing the navigational state, as
			// expected by the WSRP container implementation
			String[] navigationalState = interfaceRequest.getParameterValues(WSRPChannelURLFactory.PARAMETER_NAVIGATIONAL_STATE);
			if (navigationalState != null)
				return Collections.singletonMap(WSRPSpecKeys.NAVIGATIONAL_STATE, navigationalState);
			else
				return Collections.emptyMap();
		}
		else if (isSpecialProperty(name, PROPERTY_PUBLIC_RENDER_PARAMETERS_PREFIX))
		{
			// navigationalValues according to spec be updated from previous value, 
			// but for now will just replace, so need to eliminate null/empty values.
			// This mechanism will probably have a considerable change when
			// public render parameters are supported by Lumis Portal.
			String navigationalValues = interfaceRequest.getParameter(WSRPChannelURLFactory.PARAMETER_NAVIGATIONAL_VALUES);
			if (navigationalValues != null)
			{
				Map<String, String[]> map = com.sun.portal.wsrp.common.Util.getPublicParamMapFromNavigationValues(navigationalValues);
				for (Iterator<Map.Entry<String, String[]>> itMapEntry = map.entrySet().iterator(); itMapEntry.hasNext(); )
				{
					Map.Entry<String, String[]> entry = itMapEntry.next();
					String[] value = entry.getValue();
					if (value == null || value.length == 0)
						itMapEntry.remove();
				}
				return map;
			}
			else
			{
				return Collections.emptyMap();
			}
		}
		else
		{
			PortletSession session = interfaceRequest.getPortletSession(false);
			if (session != null)
				return session.getAttribute(name, PortletSession.APPLICATION_SCOPE);
			else
				return null;
		}
	}

	@SuppressWarnings("unchecked")
	public void setProperty(String name, Object value)
	{
		if (isSpecialProperty(name, PROPERTY_RENDER_PARAMETERS_PREFIX))
		{
			Map<String, String[]> opaqueParams = (Map<String, String[]>)value;
			String[] values = opaqueParams.get(WSRPSpecKeys.NAVIGATIONAL_STATE);
			if (values != null)
			{
				executeActionResponse.getRenderParameters().put(
						WSRPChannelURLFactory.PARAMETER_NAVIGATIONAL_STATE, values);
			}
		}
		else
		{
			interfaceRequest.getPortletSession(true).setAttribute(name, value, PortletSession.APPLICATION_SCOPE);
		}
	}
	
	/**
	 * Verifies if the property name corresponds to a special property of the
	 * given prefix.
	 * @param propertyName the property name.
	 * @param propertyPrefix the special property prefix.
	 * @return true if it corresponds to the special property; false otherwise.
	 * @since 5.5.0
	 */
	private boolean isSpecialProperty(String propertyName, String propertyPrefix)
	{
		if (propertyName.startsWith(propertyPrefix))
		{
			// check if the requested property is of the current service interface instance
			if (!propertyName.substring(propertyPrefix.length()).equals(interfaceRequest.getServiceInterfaceInstanceId()))
				throw new IllegalArgumentException("Cannot access property of another service interface instance");
			
			return true;
		}
		else
		{
			return false;
		}
	}
	
	public String getPortletHandle(String portletWindowName)
	{
		return remotePortletHandle;
	}
	
	public void setPortletHandle(String portletWindowName, String portletHandle) throws PortletWindowContextException
	{
		// TODO: must update the portlet handle for this service interface instance
		throw new UnsupportedOperationException("not implemented");
	}

	public List<EntityID> getPortletWindows(PortletType portletType, DistributionType distributionType) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public EventHolder verifySupportedPublishingEvent(EntityID portletEntityId, EventHolder eventHolder)
	{
		throw new UnsupportedOperationException();
	}

	public List<EventHolder> getSupportedPublishingEventHolders(EntityID portletEntityId)
	{
		throw new UnsupportedOperationException();
	}

	public EventHolder verifySupportedProcessingEvent(EntityID portletEntityId, EventHolder eventHolder)
	{
		throw new UnsupportedOperationException();
	}

	public List<EventHolder> getSupportedProcessingEventHolders(EntityID portletEntityId)
	{
		throw new UnsupportedOperationException();
	}

	public Map<String, String> verifySupportedPublicRenderParameters(EntityID portletEntityId, List<PublicRenderParameterHolder> publicRenderParameterHolders)
	{
		throw new UnsupportedOperationException();
	}

	public List<PublicRenderParameterHolder> getSupportedPublicRenderParameterHolders(EntityID portletEntityId, Map<String, String[]> renderParameters)
	{
		throw new UnsupportedOperationException();
	}
	
	public String encodeURL(String url)
	{
		throw new UnsupportedOperationException();
	}

	public List<String> getRoles()
	{
		throw new UnsupportedOperationException();
	}

	public Map<String, String> getUserInfo()
	{
		throw new UnsupportedOperationException();
	}

	public List<String> getMarkupTypes(String portletName) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public String getDescription(String portletName, String desiredLocale) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public String getShortTitle(String portletName, String desiredLocale) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public String getTitle(String portletName, String desiredLocale) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public List<String> getKeywords(String portletName, String desiredLocale) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public String getDisplayName(String portletName, String desiredLocale) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public String getPortletName(String portletWindowName) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public EntityID getEntityID(String portletWindowName) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public String getPortletWindowTitle(String portletWindowName, String locale) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public Map<String, String> getRoleMap(String portletWindowName) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public Map<String, String> getUserInfoMap(String portletWindowName) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public PortletPreferences getPreferences(String portletWindowName, ResourceBundle bundle, boolean isReadOnly) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public PortletLang getPortletLang(String portletWindowName) throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}

	public void store() throws PortletWindowContextException
	{
		throw new UnsupportedOperationException();
	}
	
	public String getDesktopURL(HttpServletRequest request)
	{
		throw new UnsupportedOperationException();
	}

	public String getDesktopURL(HttpServletRequest request, String query, boolean escape)
	{
		throw new UnsupportedOperationException();
	}
}
